// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/t_box_input_youtube.cpp $
// $Rev: 288 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-27 10:03:51 +0000#$

#include "t_box_input_youtube.h"
#include "t_youtube.h"

const int T_Box_Input_Youtube::v_thread_count;
const qint64 T_Box_Input_Youtube::v_target;

class T_Box_Input_YoutubePrivate
{
public:
    T_Box_Input_YoutubePrivate(T_Box_Input_Youtube *q)
        : q_ptr(q)
        , m_youtube(&m_nam)
    {
    }
    //[public]
    Q_DECLARE_PUBLIC(T_Box_Input_Youtube)
    class T_Box_Input_Youtube *q_ptr;
    //[private]
    QNetworkAccessManager m_nam;
    T_Youtube m_youtube;
    QString m_video_id;
    int m_fmt;
    qint64 m_cont_len;
    QString m_file_suffix;
    QNetworkRequest m_request;
    QByteArray m_file_bytes;
    QBuffer m_file;
    QFile m_cache_file;
    //[methods]
    QString cache_file_name()
    {
        QString v_name = m_video_id + "_" + QString::number(m_fmt);
        if(m_cont_len >= 0)
        {
            v_name += "_" + QString::number(m_cont_len);
        }
        v_name += "." + m_file_suffix;
        QString v_dirPath =
                QDesktopServices::storageLocation(
                        QDesktopServices::DesktopLocation);
        QDir dir(v_dirPath);
        QString v_filePath = dir.filePath(v_name);
        return v_filePath;
    }
};

T_Box_Input_Youtube::T_Box_Input_Youtube(const QString &a_video_id, int a_fmt)
    : m_buffer(new quint8[c_buffer_size])
{
    this->d_ptr = new T_Box_Input_YoutubePrivate(this);
    Q_D(T_Box_Input_Youtube);
    m_is_valid = true;

    d->m_video_id = a_video_id;
    d->m_fmt = a_fmt;

    QVariant v_loc = d->m_youtube.getVideoStreamUrl(d->m_video_id, d->m_fmt);
    NetworkBatch v_nbat(&d->m_nam);
    QNetworkReply *v_head_reply = NULL;
    while(v_loc.isValid())
    {
        delete v_head_reply;
        qDebug() << "[v_loc]" << v_loc;
        v_head_reply = v_nbat.head(v_loc.toUrl());
        v_loc = v_head_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    }

    QVariant v_cont_header = v_head_reply->header(QNetworkRequest::ContentTypeHeader);
    if(!v_cont_header.isValid())
    {
        qDebug() << "(!v_cont_header.isValid())";
        m_is_valid = false;
        return;
    }

    qDebug() << v_cont_header;

    if(!v_cont_header.toString().startsWith("video/"))
    {
        qDebug() << "(!v_cont_header.toString().startsWith(\"video/\"))";
        m_is_valid = false;
        return;
    }
    else
    {
        d->m_file_suffix = v_cont_header.toString();
        d->m_file_suffix.replace("video/", "");
    }

    qDebug() << "ok";

    ////Q_ASSERT(m_thread.parent()==0);

    ////QNetworkReply *v_head_reply = v_nbat.head(v_head_reply->url());
    QVariant v_cont_len = v_head_reply->header(QNetworkRequest::ContentLengthHeader);
    if(!v_cont_len.isValid())
    {
        //FIXME: case m_cont_len is not available
        d->m_cont_len = -1;
    }
    else
    {
        d->m_cont_len = v_cont_len.toLongLong();
        qDebug() << "[m_cont_len]" << d->m_cont_len;
    }

    d->m_request.setUrl(v_head_reply->url());
    d->m_request.setHeader(
            QNetworkRequest::CookieHeader,
            QVariant::fromValue(d->m_nam.cookieJar()->cookiesForUrl(v_head_reply->url()))
            );

    delete v_head_reply;

    qDebug() << "[d->cache_file_name()]" << d->cache_file_name();
    d->m_cache_file.setFileName(d->cache_file_name());
    ////if(!d->m_cache_file.open(QIODevice::ReadWrite|QIODevice::Truncate))
    if(!d->m_cache_file.open(QIODevice::ReadWrite))
    {
        qDebug() << "(!d->m_cache_file.open(QIODevice::ReadWrite))";
        m_is_valid = false;
        return;
    }

    d->m_file_bytes = d->m_cache_file.readAll();
    qDebug() << "[d->m_cache_file.size()]" << d->m_cache_file.size();
    qDebug() << "[d->m_file_bytes.size()]" << d->m_file_bytes.size();
    d->m_file.setBuffer(&d->m_file_bytes);
    d->m_file.open(QIODevice::ReadOnly);

    m_reply_queue.enqueue(new T_Box_Input_Youtube_Reply(d->m_request, d->m_file_bytes.size(), -1));
    qDebug() << "T_Box_Input_Youtube::T_Box_Input_Youtube(2)";
}

T_Box_Input_Youtube::~T_Box_Input_Youtube()
{
    //Q_D(T_Box_Input_Youtube);
    delete [] m_buffer;
    delete d_ptr;
}

bool T_Box_Input_Youtube::isValid()
{
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    return m_is_valid;
}

void T_Box_Input_Youtube::close()
{
    Q_D(T_Box_Input_Youtube);
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    if(!d->m_file.isOpen()) return;
    m_is_valid = false;
}

quint8* T_Box_Input_Youtube::getIOBuffer()
{
    return m_buffer;
}

int T_Box_Input_Youtube::getIOBufferSize()
{
    return c_buffer_size;
}

int T_Box_Input_Youtube::read_bytes(uint8_t *buf, int buf_size)
{
    Q_D(T_Box_Input_Youtube);
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    if(!isValid()) return 0;
    Q_ASSERT(d->m_file.isOpen());
    Q_ASSERT(d->m_cache_file.isOpen());

    qint64 v_diff = buf_size;
    qint64 v_avail = d->m_file.bytesAvailable();
    if(v_avail < v_diff)
    {
        int v_wait = (v_diff-v_avail);
        //qDebug() << "[v_diff]" << v_diff << "[v_avail]" << v_avail;
        //qDebug() << "[v_wait]" << v_wait;
        QByteArray v_append = this->readAll(v_wait);
        d->m_cache_file.write(v_append);
        d->m_file_bytes.append(v_append);
    }
    //QByteArray v_append = this->readAll(buf_size);
    //d->m_cache_file.write(v_append);
    //d->m_file_bytes.append(v_append);
    return d->m_file.read((char *)buf, buf_size);
}

int64_t T_Box_Input_Youtube::seek_getpos()
{
    Q_D(T_Box_Input_Youtube);
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    return d->m_file.pos();
}

int64_t T_Box_Input_Youtube::seek_setpos(int64_t offset)
{
    Q_D(T_Box_Input_Youtube);
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    if(!isValid()) return -1;
    //qDebug() << "[offset]" << offset;
    //qDebug() << "[d->m_file.pos()]" << d->m_file.pos();
    qint64 v_diff = offset - d->m_file.pos();
    if(v_diff > 0)
    {
        qint64 v_avail = d->m_file.bytesAvailable();
        if(v_avail < v_diff)
        {
            int v_wait = (v_diff-v_avail);
            //qDebug() << "[v_diff]" << v_diff << "[v_avail]" << v_avail;
            //qDebug() << "[v_wait]" << v_wait;
            QByteArray v_append = this->readAll(v_wait);
            d->m_cache_file.write(v_append);
            d->m_file_bytes.append(v_append);
        }
    }
    if(!d->m_file.seek(offset))
    {
        qDebug() << "(!m_file.seek(offset))";
        return -1;
    }
    else
    {
        return d->m_file.pos();
    }
}

int64_t T_Box_Input_Youtube::seek_length()
{
    Q_D(T_Box_Input_Youtube);
    QMutexLocker v_lock(&m_mutex);
    Q_UNUSED(v_lock);
    return d->m_cont_len;
}
